using System;
using System.Collections.Generic;

namespace Kopernikus.Parsing.LR
{
    public class LR0Item : IEquatable<LR0Item>
    {
        private readonly int m_dotPosition;
        private readonly Production m_production;

        public LR0Item(Production production, int dotPosition)
        {
            if (production == null)
            {
                throw new ArgumentNullException("production");
            }
            if (dotPosition > production.RHS.Length)
            {
                throw new ArgumentOutOfRangeException("dotPosition");
            }

            while ((dotPosition < production.RHS.Length) &&
                   production.RHS[dotPosition].HasFlags(SymbolFlags.InvalidItemDotPos))
            {
                ++dotPosition;
            }
            m_production = production;
            m_dotPosition = dotPosition;
        }

        public Production Production
        {
            get { return m_production; }
        }

        public Symbol DotSymbol
        {
            get { return m_production.RHS[m_dotPosition]; }
        }

        public bool IsComplete
        {
            get { return m_dotPosition == m_production.RHS.Length; }
        }

        #region IEquatable<LR0Item> Members

        public bool Equals(LR0Item other)
        {
            return m_dotPosition.Equals(other.m_dotPosition) && (m_production.Equals(other.m_production));
        }

        #endregion

        public bool IsDotSymbol(Symbol symbol)
        {
            return (m_dotPosition < m_production.RHS.Length) && m_production.RHS[m_dotPosition].Equals(symbol);
        }

        public LR0Item AdvanceDot()
        {
            return new LR0Item(m_production, m_dotPosition + 1);
        }

        #region Object overrides

        public override string ToString()
        {
            List<string> l =
                new List<Symbol>(m_production.RHS).ConvertAll<string>(delegate(Symbol s) { return s.ToString(); });
            l.Insert(m_dotPosition, ".");
            l.Insert(0, "->");
            l.Insert(0, m_production.LHS.ToString());

            return string.Join(" ", l.ToArray());
        }

        public override bool Equals(object obj)
        {
            return Equals(obj as LR0Item);
        }

        public override int GetHashCode()
        {
            return m_dotPosition ^ m_production.GetHashCode();
        }

        #endregion
    }
}